﻿using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;
using OpenLS.Spreadsheet.Expressions;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
    internal class DvRecord : WorksheetLevelRecord
    {
        private XLUnicodeString Error;
        private XLUnicodeString ErrorTitle;
        private XLUnicodeString Prompt;
        private XLUnicodeString PromptTitle;
        private DataValidationErrorStyle errStyle;
        private DataValidationOperator typOperator;
        private bool fAllowBlank;
        private bool fStrLookup;
        private bool fSuppressCombo;
        private DVParsedFormula formula1, formula2;
        private DataValidationImeMode mdImeMode;
        private SqRefU sqref;
        //\\    private uint firstLine;
        private DataValidationType valType;
        private uint reserved;
        private bool fShowInputMsg;
        private bool fShowErrorMsg;

        public DvRecord(DataValidation dataValidation, BiffWriterWorkbookContext context)
        {
            typOperator = dataValidation.Operator;
            this.valType = dataValidation.Type;
            this.Error = ToXlUnicodeString(dataValidation.Error);
            this.ErrorTitle = ToXlUnicodeString( dataValidation.ErrorTitle);
            this.errStyle = dataValidation.ErrorStyle;
            this.fAllowBlank = dataValidation.AllowBlank;
            ExpressionFormula expressionFormula = (ExpressionFormula) dataValidation._formula1;
            this.formula1 = ParsedFormulaBase.GetFormulaValue<DVParsedFormula>(context, expressionFormula.Expression, dataValidation.Range.TopLeftCell);
            ExpressionFormula expressionFormula2 = (ExpressionFormula)dataValidation._formula2;
            if (expressionFormula2 != null)
                this.formula2 = ParsedFormulaBase.GetFormulaValue<DVParsedFormula>(context, expressionFormula2.Expression, dataValidation.Range.TopLeftCell);
            fShowErrorMsg = dataValidation.ShowErrorMessage;
            this.fShowInputMsg = dataValidation.ShowInputMessage;
            this.fStrLookup = false;
            this.fSuppressCombo = !dataValidation.DropDownVisible;
            this.mdImeMode = dataValidation.ImeMode;
            this.Prompt = ToXlUnicodeString( dataValidation.Prompt);
            this.PromptTitle = ToXlUnicodeString( dataValidation.PromptTitle);
            this.sqref = SqRefU.FromRange(dataValidation.Range);
        }

        private XLUnicodeString ToXlUnicodeString(string str)
        {
            if (str == null)
                str = "\0";
            return (XLUnicodeString) str;
        }

        public DvRecord()
        {
        }

        private uint FirstLine
        {
            get
            {
                uint result = 0;
                result |= (uint) valType;
                result |= ((uint) errStyle << 4);
                if (fStrLookup)
                    result |= 0x80;
                if (fAllowBlank)
                    result |= 0x100;
                if (fSuppressCombo)
                    result |= 0x200;
                result |= ((uint)mdImeMode << 10);
                if (fShowInputMsg)
                    result |= 0x40000;
                if (fShowErrorMsg)
                    result |= 0x80000;
                result |= (uint)typOperator << 20;
                result |= (uint)reserved << 24;


                return result;
            }
        }

        internal override BiffRecordType RecordType
        {
            get { return BiffRecordType.Dv; }
        }

        protected override void ReadContent(BiffReaderContext context, int length)
        {
            ReadReader(context.Reader);
        }
        void ReadReader (BinaryReader reader)
        {
#if DEBUG
            ReadWriteChecker.CheckClass((t, r) => t.ReadReader(r), (t, w) => t.WriteWriter(w), reader, this);
#endif

            uint firstLine = reader.ReadUInt32();
            valType = (DataValidationType)(firstLine & 0xf);
            this.errStyle = (DataValidationErrorStyle)((firstLine >> 4) & 7);
            this.fStrLookup = ((firstLine >> 7) & 0x1) != 0;
            this.fAllowBlank = ((firstLine >> 8) & 0x1) != 0;
            this.fSuppressCombo = ((firstLine >> 9) & 0x1) != 0;
            this.mdImeMode = (DataValidationImeMode)(byte)((firstLine >> 10) & 0xff);
            this.fShowInputMsg = ((firstLine >> 18) & 0x1) != 0;
            this.fShowErrorMsg = ((firstLine >> 19) & 0x1) != 0;
            this.typOperator = (DataValidationOperator)((firstLine >> 20) & 0xF);
            this.reserved = firstLine >> 24;


            Contract.Assert(firstLine == this.FirstLine);
            PromptTitle.Read(reader);
            ErrorTitle.Read(reader);
            Prompt.Read(reader);
            Error.Read(reader);
            formula1 = new DVParsedFormula();

            formula1.ReadMe(reader);
            formula2 = new DVParsedFormula();
            formula2.ReadMe(reader);
            sqref = new SqRefU();
            sqref.Read(reader);
        }

        internal override void Apply(BiffReaderWorksheetContext context)
        {
            context.Worksheet.DataValidations.Add(ToDataValidation(context));
        }

        private DataValidation ToDataValidation(BiffReaderWorksheetContext context)
        {
            var result = new DataValidation
                             {
                                 Type = valType,
                                 AllowBlank = fAllowBlank,
                                 _formula1 = new WorksheetExpressionFormula(
                                     formula1.GetExpression(context.WorkbookContext),
                                     context.Worksheet),
                                 Error = Error.CleanString(),
                                 ErrorTitle = ErrorTitle.CleanString(),
                                 DropDownVisible = !fSuppressCombo,
                                 Range = sqref.ToRange(context.Worksheet),
                                 Operator = this.typOperator,
                                 ShowErrorMessage = fShowErrorMsg,
                                 ShowInputMessage = this.fShowInputMsg,
                             };

            Expression expression2 = formula2.GetExpression(context.WorkbookContext);
            if (expression2 != null)
                result._formula2 = new WorksheetExpressionFormula(expression2, context.Worksheet);
            return result;
        }

        internal override void WriteContent(BiffWriterWorkbookContext context)
        {
            WriteWriter(context.Writer);
        }
        void WriteWriter (BinaryWriter writer)
        {
#if DEBUG
            var position = writer.BaseStream.Position;
#endif
            writer.Write(FirstLine);
            PromptTitle.Write(writer);
            ErrorTitle.Write(writer);
            Prompt.Write(writer);
            Error.Write(writer);



            formula1.WriteMe(writer);

            if (formula2 == null)
            {
                writer.Write((ushort)0);
                writer.Write((ushort)0x33);
            }
            else
                formula2.WriteMe(writer);
            sqref.Write(writer);

        }
    }
}